﻿using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.DatabaseServices.Filters;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Geometry;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Text;
using System.Threading.Tasks;

namespace WCAD
{
    /// <summary>
    /// 和块有关的一些方法
    /// </summary>
    public static class BmBlcokMethod
    {
        /// <summary>
        /// 根据名字获取块表记录
        /// </summary>
        /// <param name="name">块名</param>
        /// <param name="btr">块表记录</param>
        /// <returns>是否有这个块表记录</returns>
        public static bool GetBlockTableRecordByName(string name, out BlockTableRecord btr)
        {
            btr = null;
            Database db = HostApplicationServices.WorkingDatabase;
            using (Transaction tr = db.TransactionManager.StartTransaction())
            {
                BlockTable bt = db.BlockTableId.GetObject(OpenMode.ForRead) as BlockTable;
                foreach (ObjectId item in bt)
                {
                    BlockTableRecord btrr = item.GetObject(OpenMode.ForRead) as BlockTableRecord;
                    if (btrr.Name == name)
                    {
                        btr = btrr;
                        return true;
                    }
                }
            }
            return false;
        }

        
        /// <summary>
        /// 插入属性块
        /// </summary>
        /// <param name="position">块基点</param>
        /// <param name="blockRecordId">块表记录id</param>
        /// <returns>块参照</returns>
        [Obsolete]
        public static BlockReference InsertAttrBlockReference(Point3d position, ObjectId blockRecordId)
        {
            BlockReference br = new BlockReference(position, blockRecordId);
            Database db = HostApplicationServices.WorkingDatabase;
            using (Transaction trans = db.TransactionManager.StartTransaction())
            {
                BlockTable bt = (BlockTable)db.BlockTableId.GetObject(OpenMode.ForWrite);
                if (bt.Has(blockRecordId))
                {
                    BlockTableRecord btr = (BlockTableRecord)bt[BlockTableRecord.ModelSpace].GetObject(OpenMode.ForWrite);
                    ObjectId id = btr.AppendEntity(br);
                    BlockTableRecord blockRecord = (BlockTableRecord)blockRecordId.GetObject(OpenMode.ForWrite);
                    trans.AddNewlyCreatedDBObject(br, true);
                    if (blockRecord.HasAttributeDefinitions)
                    {
                        foreach (ObjectId item in blockRecord)
                        {
                            DBObject obj = item.GetObject(OpenMode.ForWrite);
                            string ss = @"%<\AcExpr (%<\_FldPtr ";
                            if (obj is AttributeDefinition)
                            {

                                AttributeReference attrRef = new AttributeReference();
                                //声明属性参照

                                AttributeDefinition ad = (AttributeDefinition)obj;

                                attrRef.SetAttributeFromBlock(ad, br.BlockTransform);
                                attrRef.SetDatabaseDefaults();
                                attrRef.AdjustAlignment(WCAD.Db);
                                string str = "";
                                Field field = new Field();
                                ObjectId idd = ObjectId.Null;
                                switch (ad.Tag)
                                {
                                    case "宽":
                                        str = @"%<\AcObjProp Object(%<\_ObjId " + id.OldId.ToString() + @">%).Parameter(3).UpdatedDistance \f ""%lu2%pr0"">%";
                                        //str = @"%<\AcObjProp.16.2 Object(0).Parameter(13).UpdatedDistance \f ""%lu2%pr0"">%";
                                        field = new Field(str, true);
                                        attrRef.SetField(field);
                                        break;
                                    case "单数量":
                                        str = @"%<\AcObjProp.16.2 Object(%<\_ObjId " + id.OldId.ToString() + @">%).Parameter(3).UpdatedDistance \f ""%lu2%pr2%ct8[0.001]"">%";
                                        //str = @"%<\AcObjProp.16.2 Object(0).Parameter(13).UpdatedDistance \f ""%lu2%pr0"">%";
                                        field = new Field(str, true);
                                        attrRef.SetField(field);
                                        break;
                                    default:
                                        break;
                                }
                                ObjectId iidd = br.AttributeCollection.AppendAttribute(attrRef);
                                trans.AddNewlyCreatedDBObject(attrRef, true);
                                if (attrRef.Tag == "单数量")
                                {
                                    ss += iidd.OldId.ToString() + @">%*%<\_FldPtr ";
                                }
                                else if (attrRef.Tag == "单价")
                                {
                                    ss += iidd.OldId.ToString() + @">%*%<\_FldPtr ";
                                }
                                else if (attrRef.Tag == "折扣")
                                {
                                    ss += iidd.OldId.ToString() + @">%) \f ""%lu2%pr0"">%";
                                }
                                //idd = field.ObjectId;
                            }
                        };
                    }
                }
                trans.Commit();
            }
            return br;
        }
        /// <summary>
        /// 更新属性块的属性
        /// </summary>
        /// <param name="block">块参照</param>
        /// <param name="keyValues">更新的键值对，属性的tag和值</param>
        public static void UpdateAtt(BlockReference block, Dictionary<string, string> keyValues)
        {
            Database db = HostApplicationServices.WorkingDatabase;
            using (Transaction trans = db.TransactionManager.StartTransaction())
            {
                block.ObjectId.GetObject(OpenMode.ForRead);
                foreach (ObjectId id in block.AttributeCollection)
                {
                    AttributeReference att = id.GetObject(OpenMode.ForWrite) as AttributeReference;
                    if (keyValues.ContainsKey(att.Tag))
                    {

                        att.TextString = keyValues[att.Tag];


                    }
                }
                trans.Commit();
            }
        }
        /// <summary>
        /// 获取属性块的属性
        /// </summary>
        /// <param name="block">块参照</param>
        /// <returns>所有属性的键值对</returns>
        public static Dictionary<string, string> GetAtt(this BlockReference block)
        {
            Dictionary<string, string> keyValues = new Dictionary<string, string>();
            Database db = HostApplicationServices.WorkingDatabase;
            using (Transaction trans = db.TransactionManager.StartTransaction())
            {
                block.ObjectId.GetObject(OpenMode.ForRead);
                foreach (ObjectId id in block.AttributeCollection)
                {
                    AttributeReference att = id.GetObject(OpenMode.ForWrite) as AttributeReference;
                    keyValues.Add(att.Tag, att.TextString);
                }
                trans.Commit();
            }
            return keyValues;
        }
        /// <summary>
        /// 把一些图形生成一个database，可以用于保存图元
        /// </summary>
        /// <param name="entities">图形们</param>
        /// <param name="point">基点</param>
        /// <returns></returns>
        public static Database Wblock(List<Entity> entities, Point3d point)
        {
            ObjectIdCollection ids = new ObjectIdCollection(entities.Select(x => x.ObjectId).ToArray());
            Database db = HostApplicationServices.WorkingDatabase;
            Point3d point1 = db.Insbase;
            Database database = db.Wblock(ids, point);
            Point3d point2 = database.Insbase;
            return database;
        }
        /// <summary>
        /// 插入外部dwg
        /// </summary>
        /// <param name="sourceFileName">图纸路径</param>
        /// <param name="name">块名</param>
        /// <param name="isDelete">是否删除这个块（有时很多块放一个图纸里，只需要里面的块，并不需要这个总的块）</param>
        public static void InsertDwg(string sourceFileName, string name, bool isDelete = false)
        {
            Database database = new Database(false, true);
            if (!System.IO.File.Exists(sourceFileName)) return;
            database.ReadDwgFile(sourceFileName, FileShare.Read, true, null);
            Database db = HostApplicationServices.WorkingDatabase;
            ObjectId objid = db.Insert(name, database, true);
            if (isDelete)
            {
                using (Transaction tran = db.TransactionManager.StartTransaction())
                {
                    BlockTableRecord btr = objid.GetObject(OpenMode.ForWrite) as BlockTableRecord;
                    btr.Erase();
                    tran.Commit();
                }
            }
        }
        /// <summary>
        /// 炸块，并不会把块真的炸开，只会获取到块中的图形
        /// </summary>
        /// <param name="block">块参照</param>
        /// <param name="dyn">是否是动态块，如果动态块的话，只获取Visible</param>
        /// <returns></returns>
        public static List<Entity> ExplodeBlockReference(this BlockReference block,bool dyn=false)
        {
            DBObjectCollection dbs = new DBObjectCollection();
            block.Explode(dbs);
            List<Entity> entities = new List<Entity>();
            foreach (Entity item in dbs)
            {
                if(dyn&&item.Visible==false) continue;
                entities.Add(item);
            }
            return entities;
        }
        /// <summary>
        /// 设置动态块尺寸
        /// </summary>
        /// <param name="block">块参照</param>
        /// <param name="propNameValues">尺寸值键值对</param>
        public static void SetDynBlockValue(BlockReference block, Dictionary<string, double> propNameValues)
        {
            Database db = HostApplicationServices.WorkingDatabase;
            using (Transaction trans = db.TransactionManager.StartTransaction())
            {
                block.ObjectId.GetObject(OpenMode.ForWrite);
                DynamicBlockReferencePropertyCollection dynProperties = block.DynamicBlockReferencePropertyCollection;

                foreach (DynamicBlockReferenceProperty property in dynProperties)
                {
                    if (property.ReadOnly || property.PropertyName == "Origin")
                    {
                        continue;
                    }
                    string name = property.PropertyName;
                    if (name == "高")
                    {
                        WCAD.Ed.WriteMessage(property.BlockId.OldId.ToString() + "\n");
                    }
                    if (name == "长")
                    {
                        WCAD.Ed.WriteMessage(property.BlockId.OldId.ToString());
                    }
                    if (propNameValues.ContainsKey(name))
                    {
                        property.Value = propNameValues[name];
                    }
                }
                trans.Commit();
            }
        }
        /// <summary>
        /// 设置单个动态块尺寸
        /// </summary>
        /// <param name="block">块参照</param>
        /// <param name="name">尺寸名</param>
        /// <param name="value">尺寸值</param>
        public static void SetDynBlockValue(BlockReference block, string name, double value)
        {
            Dictionary<string, double> keyValuePairs = new Dictionary<string, double>();
            keyValuePairs.Add(name, value);
            SetDynBlockValue(block, keyValuePairs);
        }
        /// <summary>
        /// 获取动态块的值
        /// </summary>
        /// <param name="block">块参照</param>
        /// <param name="name">名称</param>
        /// <returns></returns>
        public static object GetDynBlockValue(BlockReference block, string name)
        {
            Database db = HostApplicationServices.WorkingDatabase;
            using (Transaction trans = db.TransactionManager.StartTransaction())
            {
                block.ObjectId.GetObject(OpenMode.ForWrite);
                DynamicBlockReferencePropertyCollection dynProperties = block.DynamicBlockReferencePropertyCollection;

                foreach (DynamicBlockReferenceProperty property in dynProperties)
                {
                    if (property.ReadOnly || property.PropertyName == "Origin")
                    {
                        continue;
                    }
                    if (name == property.PropertyName) return property.Value;
                }
            }
            return null;
        }
        public static List<string> GetDynBlockValues(this BlockReference block)
        {
            List<string> result = new List<string>();
            Database db = HostApplicationServices.WorkingDatabase;
            using (Transaction trans = db.TransactionManager.StartTransaction())
            {
                block.ObjectId.GetObject(OpenMode.ForWrite);
                DynamicBlockReferencePropertyCollection dynProperties = block.DynamicBlockReferencePropertyCollection;

                foreach (DynamicBlockReferenceProperty property in dynProperties)
                {
                    if (property.PropertyTypeCode == 5)
                    {
                        var ss = property.GetAllowedValues();
                        foreach (var item in ss)
                        {
                            result.Add(item.ToString());
                        }
                    }
                }
            }
            return result;
        }
        public static bool SetDynBlockValues(this BlockReference block,string name)
        {
            List<string> result = new List<string>();
            Database db = HostApplicationServices.WorkingDatabase;
            using (Transaction trans = db.TransactionManager.StartTransaction())
            {
                block.ObjectId.GetObject(OpenMode.ForWrite);
                DynamicBlockReferencePropertyCollection dynProperties = block.DynamicBlockReferencePropertyCollection;

                foreach (DynamicBlockReferenceProperty property in dynProperties)
                {
                    if (property.PropertyTypeCode == 5)
                    {
                        var ss = property.GetAllowedValues();
                        if (ss.Contains(name))
                        {
                            property.Value = name;
                            trans.Commit();
                            return true;
                        }
                    }
                }
            }
            return false;
        }
        /// <summary>
        /// 判断一个块是否为动态块
        /// </summary>
        /// <param name="block">块参照</param>
        /// <returns></returns>
        public static bool IsDynBlock(BlockReference block)
        {
            Database db = HostApplicationServices.WorkingDatabase;
            using (Transaction trans = db.TransactionManager.StartTransaction())
            {
                block.ObjectId.GetObject(OpenMode.ForWrite);
                return block.IsDynamicBlock;
            }
        }
        /// <summary>
        /// 判断一个块是否为xc块
        /// </summary>
        /// <param name="block">块参照</param>
        /// <returns></returns>
        public static bool IsXCBlcok(BlockReference block)
        {
            Database db = HostApplicationServices.WorkingDatabase;
            if (block.ExtensionDictionary != ObjectId.Null)
            {
                using (Transaction transaction = HostApplicationServices.WorkingDatabase.TransactionManager.StartTransaction())
                {
                    DBDictionary ent = (DBDictionary)block.ExtensionDictionary.GetObject(OpenMode.ForWrite);
                    foreach (var item in ent)
                    {
                        if (item.Key != "ACAD_FILTER") continue;
                        ObjectId objectId = item.Value;
                        DBDictionary ent1 = (DBDictionary)objectId.GetObject(OpenMode.ForWrite);
                        foreach (var item1 in ent1)
                        {
                            if (item1.Key == "SPATIAL")
                            {
                                return true;
                            }
                        }
                    }
                }
            }
            return false;
        }
        /// <summary>
        /// 获取xc块的SpatialFilter
        /// </summary>
        /// <param name="block">块参照</param>
        /// <returns></returns>
        public static SpatialFilter TryGetSpatialFilter(BlockReference block)
        {
            using (Transaction trans = HostApplicationServices.WorkingDatabase.TransactionManager.StartTransaction())
            {
                SpatialFilter spatialFilter = null;
                string textString = string.Empty;
                block.ObjectId.GetObject(OpenMode.ForWrite);
                if (!block.ExtensionDictionary.IsNull)
                {
                    DBDictionary ExtensionDic = block.ExtensionDictionary.GetObject(OpenMode.ForRead) as DBDictionary;
                    foreach (var item in ExtensionDic)
                    {
                        DBObject BorderEnt = item.Value.GetObject(OpenMode.ForWrite);
                        if (BorderEnt is DBDictionary)
                        {
                            foreach (var item2 in BorderEnt as DBDictionary)
                            {
                                DBObject BorderEnt1 = (DBObject)trans.GetObject(item2.Value, OpenMode.ForRead, true);
                                if (BorderEnt1 is SpatialFilter)
                                {
                                    spatialFilter = (SpatialFilter)BorderEnt1;
                                    break;
                                }
                            }
                        }
                    }
                }
                return spatialFilter;
            }

        }
        /// <summary>
        /// 根据SpatialFilter获取多段线
        /// </summary>
        /// <param name="spatialFilter"></param>
        /// <returns></returns>
        public static Polyline GetPolyline(this SpatialFilter spatialFilter)
        {
            Polyline polyline = new Polyline();
            int n = 0;
            foreach (Point2d item in spatialFilter.Definition.GetPoints())
            {
                polyline.AddVertexAt(n, item, 0, 0, 0);
                n++;
            }
            return polyline;
        }
        /// <summary>
        /// 给一个块添加xc
        /// </summary>
        /// <param name="block">块参照</param>
        /// <param name="polyline">xc边界</param>
        /// <returns></returns>
        public static bool SetXC(this BlockReference block, Polyline polyline)
        {
            Database db = HostApplicationServices.WorkingDatabase;
            using (Transaction trans = db.TransactionManager.StartTransaction())
            {
                //SpatialFilter spatialFilter = null;
                string textString = string.Empty;
                block.ObjectId.GetObject(OpenMode.ForWrite);
                if (block.ExtensionDictionary.IsNull)
                {
                    block.CreateExtensionDictionary();
                }
                DBDictionary extensionDic = block.ExtensionDictionary.GetObject(OpenMode.ForWrite) as DBDictionary;
                SpatialFilterDefinition definition = new SpatialFilterDefinition(polyline.GetPoint2dCollection(), Vector3d.ZAxis, 0, 1E+300, 1E+300, true );
                SpatialFilter spatial = new SpatialFilter();
                spatial.SetPerspectiveCamera(new Point3d());
                spatial.Definition = definition;
                DBDictionary dbdic = new DBDictionary();
                extensionDic.SetAt("ACAD_FILTER", dbdic);
                dbdic.ObjectId.GetObject(OpenMode.ForWrite);
                dbdic.SetAt("SPATIAL", spatial);
                
                trans.AddNewlyCreatedDBObject(dbdic, true);
                trans.AddNewlyCreatedDBObject(spatial, true);
                trans.Commit();
                return true;
            }

        }
    }
}
